package idgenerator

import (
	"github.com/pingcap/errors"
	"io"
	"strconv"
)

type Reply io.WriterTo

type ErrorReply struct {
	message string
}

func (er *ErrorReply) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte("-ERROR " + er.message + "\r\n"))
	return int64(n), err
}

func (er *ErrorReply) Error() string {
	return er.message
}

type PongReply struct {
}

func (r *PongReply) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte("+PONG\r\n"))
	return int64(n), err
}

type StatusReply struct {
	code string
}

func (r *StatusReply) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte("+" + r.code + "\r\n"))
	return int64(n), err
}

type EmptyReply struct {
}

func (r *EmptyReply) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte("$-1\r\n"))
	return int64(n), err
}

type IntReply struct {
	number int64
}

func (r *IntReply) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte(":" + strconv.FormatInt(r.number, 10) + "\r\n"))
	return int64(n), err
}

type BulkReply struct {
	value []byte
}

func (r *BulkReply) WriteTo(w io.Writer) (int64, error) {
	return writeBytes(r.value, w)
}

type MultiBulkReply struct {
	values [][]byte
}

func (r *MultiBulkReply) WriteTo(w io.Writer) (int64, error) {
	if r.values == nil {
		return 0, errors.Errorf("Multi bulk reply found a nil values")
	}
	if wrote, err := w.Write([]byte("*" + strconv.Itoa(len(r.values)) + "\r\n")); err != nil {
		return int64(wrote), err
	} else {
		total := int64(wrote)
		for _, value := range r.values {
			wroteData, err := writeBytes(value, w)
			total += wroteData
			if err != nil {
				return total, err
			}
		}
		return total, nil
	}
}

func writeNullBytes(w io.Writer) (int64, error) {
	n, err := w.Write([]byte("$-1\r\n"))
	return int64(n), err
}

func writeBytes(value interface{}, w io.Writer) (int64, error) {
	if value == nil {
		return writeNullBytes(w)
	}
	switch v := value.(type) {
	case []byte:
		if len(v) == 0 {
			return writeNullBytes(w)
		}
		buf := []byte("$" + strconv.Itoa(len(v)) + "\r\n")
		buf = append(buf, v...)
		buf = append(buf, []byte("\r\n")...)
		n, err := w.Write(buf)
		if err != nil {
			return 0, err
		}
		return int64(n), nil
	case int:
		wrote, err := w.Write([]byte(":" + strconv.Itoa(v) + "\r\n"))
		return int64(wrote), err
	}
	return 0, errors.Errorf("Invalid type sent to WriteBytes")
}
